interface ClickOutsideData {
  el: Element
  binding: any
  handler?(e: MouseEvent): void
}

// 判断第二个参数是否是第一个参数的子节点
function judgeChild(parent: Element, child: Element): boolean {
  if (parent.children.length === 0) return false

  return Array.from(parent.children).some(_child => _child === child || judgeChild(_child, child))
}

// 数据列表
const clickOutsideDataList: ClickOutsideData[] = []

// document 点击事件
function handleDocumentClick(this: ClickOutsideData, e: MouseEvent) {
  const el: Element = this.el
  const binding = this.binding
  const callback: Function = (binding.value as Function)
  const target = (e.target as Element)
  if (el !== target && !judgeChild(el, target)) {
    callback && typeof callback === 'function' && callback()
  }
}

export default {
  bind: function(el: Element, binding: any) {
    const callback: Function = (binding.value as Function)

    if (!callback || typeof callback !== 'function') {
      throw new Error('v-click-outside 需要赋值 Function')
    }

    // 设置数据
    const clickOutsideData: ClickOutsideData = { el, binding }
    clickOutsideData.handler = handleDocumentClick.bind(clickOutsideData)
    // 绑定事件
    document.addEventListener('click', clickOutsideData.handler)
    // 加入数据列表
    clickOutsideDataList.push(clickOutsideData)
  },
  unbind: function(el: Element) {
    // 找到数据
    const findIndex = clickOutsideDataList.findIndex(item => item.el === el)
    const findObj = findIndex >= 0 ? clickOutsideDataList[findIndex] : null
    if (findObj && findObj.handler) {
      // 解绑事件
      document.removeEventListener('click', findObj.handler)
    }
    // 移出数据列表
    clickOutsideDataList.splice(findIndex, 1)
  },
}
